The Idempotency HTTP Header Field
POSTリクエストを冪等処理可能にするIdempotency-Keyヘッダの提案仕様 2020
クライアントはランダムな値(UUID)などを、ヘッダ値をセットします。
サーバはこのIdempotency-Keyを元に複数回同じリクエストを受け取っても冪等処理を行います。2回目以降のリクエストに対しては、初回と同じレスポンスを返します。
サーバは一定期間後に受け取ったIdempotency-Keyの値を忘却しても問題ありません。
https://datatracker.ietf.org/doc/html/draft-idempotency-header-01
https://datatracker.ietf.org/doc/html/draft-ietf-httpapi-idempotency-key-header-00
POST, PATCHは冪等ではないとRFC7231で定められている
https://tools.ietf.org/html/rfc7231
keyについて
code:syntax
The "Idempotency-Key" request header field describes
Idempotency-Key = idempotency-key-value
idempotency-key-value = opaque-value
opaque-value = DQUOTE *idempotencyvalue DQUOTE
idempotencyvalue = %x21 / %x23-7E / obs-text
; VCHAR except double quotes, plus obs-text
Clients MUST NOT include more than one "Idempotency-Key" header field
in the same request.
uniqueness
異なるリクエストペイロードには利用してはいけない
uniquenessの保証の仕方はリソースオーナが提示し、クライアントはそのとおりに実装しなければならない
UUIDを推奨する
validity expiry
time based idempotency key かもしれない
リソースサーバはexpiryに関するポリシーを予め表明すべき
idempotency fingerprint
idempotencyフィンガープリントは、リクエストの一意性を決定するために idempotencyキーと組み合わせて使用してもよい
idempotencyフィンガープリントはリソースの実装によって生成される
idempotencyフィンガープリント生成アルゴリズムは、フィンガープリントを生成するために、以下のいずれかまたは類似のアプローチを使用してもよい
リクエスト・ペイロード全体のチェックサム
リクエストペイロード内の選択された要素のチェックサム
リクエスト ペイロード内の各フィールドのフィールド値の一致
リクエストペイロード内の選択された要素のフィールド値の一致
リクエスト・ダイジェスト/署名
Idempotency Enforcement Scenarios
サーバで未知のkeyが来たとき
通常通りエンドポイントの処理する
既知のkeyが来たとき
Retry
最初のリクエストが完了したあとにretryリクエストが来た場合、既に完了したリクエストと同一のレスポンスを返す
Concurrent Request
最初のリクエストが完了する前にretryリクエストが来た場合、サーバはresource conflict errorを返す
Error Scenarios
Idempotency-Key headerが要求されるエンドポイントでheaderが送られなかった場合
status code 400を返す
リンクを添える
code:response
HTTP/1.1 400 Bad Request
Link: <https://developer.example.com/idempotency>;
rel="describedby"; type="text/html"
異なるリクエストペイロードでidempotency keyが再利用された場合
code:response
HTTP/1.1 422 Unprocessable Entity
Link: <https://developer.example.com/idempotency>;
rel="describedby"; type="text/html"
期限切れのidempotency keyが再利用された場合 draft v01 からは削除された
code:response
HTTP/1.1 422 Unprocessable Entity
Link: <https://developer.example.com/idempotency>;
rel="describedby"; type="text/html"
最初のリクエストが完了する前にreplayリクエストが来た場合
code:response
HTTP/1.1 409 Conflict
Link: <https://developer.example.com/idempotency>;
rel="describedby"; type="text/html"
それ以外のエラー
validation errorとか
適切に処理する
既存の実装
Organization: Stripe
Description: Stripe uses custom HTTP header named "Idempotency-Key"
Reference: https://stripe.com/docs/idempotency
Organization: Adyen
Description: Adyen uses custom HTTP header named "Idempotency-Key"
Reference: https://docs.adyen.com/development-resources/api-idempotency/
Organization: Dwolla
Description: Dwolla uses custom HTTP header named "Idempotency-Key"
Reference: https://docs.dwolla.com/
Organization: Interledger
Description: Interledger uses custom HTTP header named "Idempotency-Key"
Reference: https://github.com/interledger/
Organization: WorldPay
Description: WorldPay uses custom HTTP header named "Idempotency-Key"
Reference: https://developer.worldpay.com/docs/wpg/idempotency
Organization: Yandex
Description: Yandex uses custom HTTP header named "Idempotency-Key"
Reference: https://cloud.yandex.com/docs/api-design-guide/concepts/idempotency
コンセプトの実装
Organization: Django
Description: Django uses custom HTTP header named "HTTP_IDEMPOTENCY_KEY"
Reference: https://pypi.org/project/django-idempotency-key
Organization: Twilio
Description: Twilio uses custom HTTP header named "I-Twilio-Idempotency-Token" in webhooks
Reference: https://www.twilio.com/docs/usage/webhooks/webhooks-connection-overrides
Organization: PayPal
Description: PayPal uses custom HTTP header named "PayPal-Request-Id"
Reference: https://developer.paypal.com/docs/business/develop/idempotency
Organization: RazorPay
Description: RazorPay uses custom HTTP header named "X-Payout-Idempotency"
Reference: https://razorpay.com/docs/razorpayx/api/idempotency/
Organization: OpenBanking
Description: OpenBanking uses custom HTTP header called "x-idempotency-key"
Reference: https://openbankinguk.github.io/read-write-api-site3/v3.1.6/profiles/read-write-data-api-profile.html#request-headers
Organization: Square
Description: To make an idempotent API call, Square recommends adding a property named "idempotency_key" with a unique value in the request body.
Reference: https://developer.squareup.com/docs/build-basics/using-rest-api
Organization: Google Standard Payments
Description: Google Standard Payments API uses a property named "requestId" in request body in order to provider idempotency in various use cases.
Reference: https://developers.google.com/standard-payments/payment-processor-service-api/rest/v1/TopLevel/capture
Organization: BBVA
Description: BBVA Open Platform uses custom HTTP header called "X-Unique-Transaction-ID"
Reference: https://bbvaopenplatform.com/apiReference/APIbasics/content/x-unique-transaction-id
Organization: WebEngage
Description: WebEngage uses custom HTTP header called "x-request-id" to identify webhook POST requests uniquely to achieve events idempotency.
Reference: https://docs.webengage.com/docs/webhooks
Security consideration
リソースサーバはidempotency keyのフォーマットを公開し、リクエスト処理前に検証を行うべき